/*
 * 描          述:  <描述>
 * 修  改   人:  PengQingyang
 * 修改时间:  2024年3月10日
 * <修改描述:>
 */
package com.tx.main.zctest;

import com.alibaba.fastjson2.JSONObject;
import com.opencsv.CSVReader;
import com.opencsv.exceptions.CsvException;
import com.tx.core.ddlutil.builder.DDLBuilder;
import com.tx.core.ddlutil.builder.alter.AlterTableDDLBuilder;
import com.tx.core.ddlutil.builder.create.CreateTableDDLBuilder;
import com.tx.core.ddlutil.executor.impl.H2TableDDLExecutor;
import com.tx.core.exceptions.SILException;
import com.tx.core.exceptions.util.AssertUtils;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;

import javax.sql.DataSource;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.math.BigInteger;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <功能简述>
 * <功能详细描述>
 *
 * @author PengQingyang
 * @version [版本号, 2024年3月10日]
 * @see [相关类/方法]
 * @since [产品/模块版本]
 */
public class Main3 {

    public static void main(String[] args) {
        DataSource ds = DataSourceBuilder.create().driverClassName("org.h2.Driver").url("jdbc:h2:file:D:/database/test1;MODE=MYSQL").username("root").password("root").build();
        H2TableDDLExecutor ddlExecutor = new H2TableDDLExecutor(ds);
        ddlExecutor.afterPropertiesSet();

        //装载配置文件
        SAXBuilder builder = new SAXBuilder();
        Document document = null;
        try {
            document = builder.build(new File("d:/test/config.cfg"));
        } catch (JDOMException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        Element root = document.getRootElement();
        List<Element> children = root.getChildren("变量");
        List<ItemCfg> varCfg100 = children.stream().filter(e -> {
            if ("100ms".equals(e.getChildText("记录周期"))) {
                return true;
            }
            return false;
        }).map(e -> {
            ItemCfg ic = new ItemCfg();
            ic.setId(Integer.parseInt(e.getChildText("索引号")));
            ic.setName(e.getChildText("变量名"));
            ic.setVarName(e.getChildText("记录变量"));
            ic.setOrdinal(NumberUtils.toInt(e.getChildText("常用变量顺序"), 0));
            ic.setDirection(ItemDirectionTypeEnum.valueOf(e.getChildText("方向")));
            ic.setPeriod(e.getChildText("记录周期"));
            ic.setByteOffset(e.getChildText("字偏移").isEmpty() ? 0 : Integer.parseInt(e.getChildText("字偏移")));
            ic.setBytePosOffset(e.getChildText("字节位偏移").isEmpty() ? 0 : Integer.parseInt(e.getChildText("字节位偏移")));
            ic.setRequired(Boolean.parseBoolean(e.getChildText("必须记录")));
            ic.setSystem(e.getChildText("系统"));
            ic.setType(ItemCfgTypeEnum.valueOf(e.getChildText("变量类型")));
            return ic;
        }).collect(Collectors.toList());
        List<ItemCfg> varCfg500 = children.stream().filter(e -> {
            if ("500ms".equals(e.getChildText("记录周期"))) {
                return true;
            }
            return false;
        }).map(e -> {
            ItemCfg ic = new ItemCfg();
            ic.setId(Integer.parseInt(e.getChildText("索引号")));
            ic.setName(e.getChildText("变量名"));
            ic.setVarName(e.getChildText("记录变量"));
            ic.setOrdinal(NumberUtils.toInt(e.getChildText("常用变量顺序"), 0));
            ic.setDirection(ItemDirectionTypeEnum.valueOf(e.getChildText("方向")));
            ic.setPeriod(e.getChildText("记录周期"));
            ic.setByteOffset(e.getChildText("字偏移").isEmpty() ? 0 : Integer.parseInt(e.getChildText("字偏移")));
            ic.setBytePosOffset(e.getChildText("字节位偏移").isEmpty() ? 0 : Integer.parseInt(e.getChildText("字节位偏移")));
            ic.setRequired(Boolean.parseBoolean(e.getChildText("必须记录")));
            ic.setSystem(e.getChildText("系统"));
            ic.setType(ItemCfgTypeEnum.valueOf(e.getChildText("变量类型")));
            return ic;
        }).collect(Collectors.toList());
        //<words50>1</words50>
        //<words100>173</words100>
        //<words500>811</words500>
        int cfgCellCountOf100 = Integer.parseInt(root.getChildText("words100"));
        int cfgCellCountOf500 = Integer.parseInt(root.getChildText("words500"));

        System.out.println("varCfg500.size：" + varCfg500.size());
        System.out.println("varCfg100.size：" + varCfg100.size());

        Map<String, MultiValueMap<ItemDirectionTypeEnum, ItemCfg>> cfg500Map = new HashMap<>();
        varCfg500.stream().forEach(c -> {
            if (!cfg500Map.containsKey(c.getSystem())) {
                cfg500Map.put(c.getSystem(), new LinkedMultiValueMap<>());
            }
            cfg500Map.get(c.getSystem()).add(c.getDirection(), c);
        });
        for (Map.Entry<String, MultiValueMap<ItemDirectionTypeEnum, ItemCfg>> entryTemp : cfg500Map.entrySet()) {
            System.out.println("系统：" + entryTemp.getKey());
            entryTemp.getValue().forEach((k, v) -> {
                System.out.println("方向：" + k);
                /*v.forEach(c->{
                    System.out.println("变量名：" + c.getName());
                });*/
                System.out.println("项数：" + v.size());
                String tableName = ("t_" + entryTemp.getKey() + "_" + k.name().toLowerCase()).toLowerCase().replaceAll("-","_");

                DDLBuilder<?> ddlBuilder = null;
                if (ddlExecutor.exists(tableName)) {
                    ddlBuilder = ddlExecutor
                            .generateAlterTableDDLBuilder(tableName);
                } else {
                    ddlBuilder = ddlExecutor
                            .generateCreateTableDDLBuilder(tableName);
                }
                ddlBuilder.setPrimaryKeyColumnNames("id");
                ddlBuilder.newColumnOfVarchar("id", 64, true, null);
                DDLBuilder<?> ddlBuilderFinal = ddlBuilder;

                v.stream().forEach(ic -> {
                    switch (ic.getType()) {
                        case INTEGER16:
                            ddlBuilderFinal.newColumnOfInteger(ic.getVarName(), 16, false, null);
                            break;
                        case UNSIGNED16:
                            ddlBuilderFinal.newColumnOfVarchar(ic.getVarName(), 16, false, null);
                            break;
                        case BOOLEAN1:
                            ddlBuilderFinal.newColumnOfBoolean(ic.getVarName(), false, null);
                            break;
                        case UNSIGNED8:
                            ddlBuilderFinal.newColumnOfInteger(ic.getVarName(), false, null);
                            break;
                        case UNSIGNED32:
                            ddlBuilderFinal.newColumnOfInteger(ic.getVarName(), 32, false, null);
                            break;
                        default:
                            System.out.println("error. 不支持的数据类型:" + ic.getType() + " | cellData:" + ic);
                            break;
                    }
                });
                if (AlterTableDDLBuilder.class.isInstance(ddlBuilder)) {
                    System.out.println("检测到需要升级.task_execute_log");
                    ddlExecutor.alter((AlterTableDDLBuilder)ddlBuilder);
                } else{
                    System.out.println("创建表成功.task_execute_log");
                    ddlExecutor.create((CreateTableDDLBuilder) ddlBuilder);
                }
            });
        }

        String filePath = "d:/test/RundataSlow_2022_07_19_19.csv";
        try {
            List<Map<String, JSONObject>> rowMapList = parseSlowCSV(filePath, varCfg500, cfgCellCountOf500);

            System.out.println(rowMapList.get(0));
            System.out.println(rowMapList.get(0).size());
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (CsvException e) {
            throw new RuntimeException(e);
        }

        //RundataFast_2022_07_19_19.csv
        //RundataSlow_2022_07_19_19.csv
        //"d:/test/RundataSlow_2022_07_19_19.csv"
        filePath = "d:/test/RundataFast_2022_07_19_19.csv";

        //读取csv文件
        // 创建文件读取流
        try {
            List<Map<String, JSONObject>> rowMapList = parseFastCSV(filePath, varCfg100, cfgCellCountOf100);

            System.out.println(rowMapList.get(0));
            System.out.println(rowMapList.get(0).size());
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (CsvException e) {
            throw new RuntimeException(e);
        }


    }

    private static List<Map<String, JSONObject>> parseFastCSV(String filePath, List<ItemCfg> fastVarCfg,
                                                              int fastLength) throws IOException, CsvException {
        Date startDate = new Date();

        FileReader fileReader = new FileReader(filePath);
        // 创建CSV读取器
        CSVReader csvReader = new CSVReader(fileReader);
        // 读取CSV文件的所有行
        List<String[]> lines = csvReader.readAll();
        System.out.println("csvReader.readAll耗时：" + ((new Date()).getTime() - startDate.getTime()) + "豪秒");
        startDate = new Date();

        List<Map<String, JSONObject>> resList = new ArrayList<>();
        lines.parallelStream().forEach(line -> {
            if (line.length < fastLength) {
                //第一行有可能不能读取
                return;
            }

            Map<String, JSONObject> rowMap = new HashMap<>();
            resList.add(rowMap);
            Map<Integer, String> tempBinaryMap = new HashMap<>();
            for (ItemCfg ic : fastVarCfg) {
                int byteOffset = ic.getByteOffset() + 2;
                String cellData = line[byteOffset];
                String sys = ic.getSystem();
                JSONObject jot = null;
                if (rowMap.containsKey(sys)) {
                    jot = rowMap.get(sys);
                } else {
                    jot = new JSONObject();
                    rowMap.put(sys, jot);
                }
                try {
                    switch (ic.getType()) {
                        case INTEGER16:
                            jot.put(ic.getVarName(), hexToINTEGER16(cellData));
                            break;
                        case UNSIGNED16:
                            jot.put(ic.getVarName(), hexToUNSIGNED16(cellData));
                            break;
                        case BOOLEAN1:
                            String binaryFB = null;
                            if (!tempBinaryMap.containsKey(byteOffset)) {
                                binaryFB = hexToBINARYOF16(cellData);
                            } else {
                                binaryFB = tempBinaryMap.get(byteOffset);
                            }
                            jot.put(ic.getVarName(), hexToBOOLEAN1(binaryFB, ic.getBytePosOffset()));
                            break;
                        case UNSIGNED8:
                            String binaryFU = null;
                            if (!tempBinaryMap.containsKey(byteOffset)) {
                                binaryFU = hexToBINARYOF16(cellData);
                            } else {
                                binaryFU = tempBinaryMap.get(byteOffset);
                            }
                            jot.put(ic.getVarName(), hexToUNSIGNED8(binaryFU, ic.getBytePosOffset()));
                            break;
                        case UNSIGNED32:
                            jot.put(ic.getVarName(), hexToUNSIGNED32(cellData));
                            break;
                        default:
                            System.out.println("error. 不支持的数据类型:" + ic.getType() + " | cellData:" + cellData);
                            break;
                    }
                } catch (Exception e) {
                    System.out.println("error. :" + ic.getType() + " | cellData:" + cellData + " | 字节位偏移:" + ic.getByteOffset() +
                            " | error message:" + e.getMessage());
                    e.printStackTrace();
                }
            }
        });
        System.out.println("解析耗时：" + ((new Date()).getTime() - startDate.getTime()) + "豪秒");
        return resList;
    }

    private static List<Map<String, JSONObject>> parseSlowCSV(String filePath, List<ItemCfg> slowVarCfg,
                                                              int slowLength) throws IOException, CsvException {
        Date startDate = new Date();

        FileReader fileReader = new FileReader(filePath);
        // 创建CSV读取器
        CSVReader csvReader = new CSVReader(fileReader);
        // 读取CSV文件的所有行
        List<String[]> lines = csvReader.readAll();

        System.out.println("slowcsv  |  csvReader.readAll耗时：" + ((new Date()).getTime() - startDate.getTime()) +
                "毫秒");
        startDate = new Date();

        List<Map<String, JSONObject>> resList = new ArrayList<>();
        lines.parallelStream().forEach(line -> {
            if (line.length < slowLength) {
                //第一行有可能不能读取
                return;
            }

            Map<String, JSONObject> rowMap = new HashMap<>();
            resList.add(rowMap);
            Map<Integer, String> tempBinaryMap = new HashMap<>();
            for (ItemCfg ic : slowVarCfg) {
                int byteOffset = ic.getByteOffset() + 2;
                String cellData = line[byteOffset];
                String sys = ic.getSystem();
                JSONObject jot = null;
                if (rowMap.containsKey(sys)) {
                    jot = rowMap.get(sys);
                } else {
                    jot = new JSONObject();
                    rowMap.put(sys, jot);
                }
                try {
                    switch (ic.getType()) {
                        case INTEGER16:
                            jot.put(ic.getVarName(), hexToINTEGER16(cellData));
                            break;
                        case UNSIGNED16:
                            jot.put(ic.getVarName(), hexToUNSIGNED16(cellData));
                            break;
                        case BOOLEAN1:
                            String binaryFB = null;
                            if (!tempBinaryMap.containsKey(byteOffset)) {
                                binaryFB = hexToBINARYOF16(cellData);
                                tempBinaryMap.put(byteOffset, binaryFB);
                            } else {
                                binaryFB = tempBinaryMap.get(byteOffset);
                            }
                            jot.put(ic.getVarName(), hexToBOOLEAN1(binaryFB, ic.getBytePosOffset()));
                            break;
                        case UNSIGNED8:
                            String binaryFU = null;
                            if (!tempBinaryMap.containsKey(byteOffset)) {
                                binaryFU = hexToBINARYOF16(cellData);
                                tempBinaryMap.put(byteOffset, binaryFU);
                            } else {
                                binaryFU = tempBinaryMap.get(byteOffset);
                            }
                            jot.put(ic.getVarName(), hexToUNSIGNED8(binaryFU, ic.getBytePosOffset()));
                            break;
                        case UNSIGNED32:
                            jot.put(ic.getVarName(), hexToUNSIGNED32(cellData));
                            break;
                        default:
                            System.out.println("error. 不支持的数据类型:" + ic.getType() + " | cellData:" + cellData);
                            break;
                    }
                } catch (Exception e) {
                    System.out.println("error. :" + ic.getType() + " | cellData:" + cellData + " | 字节位偏移:" + ic.getByteOffset() +
                            " | error message:" + e.getMessage());
                    e.printStackTrace();
                }
            }
        });
        System.out.println("解析耗时：" + ((new Date()).getTime() - startDate.getTime()) + "毫秒");
        return resList;
    }

    /**
     * 解析16进制数位32位无符号整形数据
     *
     * @param cellData
     * @return
     */
    private static String hexToINTEGER16(String cellData) {
        //cellData = preProcess(cellData);
        BigInteger value = new BigInteger(cellData, 16);
        // 补充高位到32位
        return value.toString(10);
    }

    /**
     * 解析16进制数位32位无符号整形数据
     *
     * @param cellData
     * @return
     */
    private static String hexToUNSIGNED16(String cellData) {
        //cellData = preProcess(cellData);
        BigInteger value = new BigInteger(cellData, 16);
        return value.toString(10);
    }

    /**
     * 解析16进制数位32位无符号整形数据
     *
     * @param cellData
     * @return
     */
    private static String hexToUNSIGNED32(String cellData) {
        //cellData = preProcess(cellData);
        BigInteger value = new BigInteger(cellData, 16);
        return value.toString(10);
    }

    private static String hexToBOOLEAN1(String cellDataBinaryStr, int cellDataIndex) {
        return String.valueOf(BooleanUtils.toBoolean(cellDataBinaryStr.substring(cellDataIndex, cellDataIndex + 1)));
    }

    /**
     * 解析UNSIGNED8
     *
     * @param cellDataBinaryStr
     * @param cellDataIndex
     * @return
     */
    private static String hexToUNSIGNED8(String cellDataBinaryStr, int cellDataIndex) {
        String temp = "";
        switch (cellDataIndex) {
            case 0:
                temp = StringUtils.substring(cellDataBinaryStr, 0, 8);
                return (new BigInteger(temp, 2)).toString(10);
            case 1:
                temp = StringUtils.substring(cellDataBinaryStr, 8, 16);
                return (new BigInteger(temp, 2)).toString(10);
            default:
                throw new SILException("hexToUNSIGNED8 error. cellDataIndex:" + cellDataIndex + "|cellDataBinaryStr:" + cellDataBinaryStr);
        }
    }

    /**
     * 转换位16位的二进制数
     *
     * @param cellData
     * @return
     */
    private static String hexToBINARYOF16(String cellData) {
        //cellData = preProcess(cellData);
        BigInteger value = new BigInteger(cellData, 16);
        String binaryString = value.toString(2);
        //补充高位到16位
        return String.format("%16s", binaryString).replace(' ', '0');
    }

    /**
     * 预处理数据
     *
     * @param cellData
     * @return
     */
    private static String preProcess(String cellData) {
        AssertUtils.notEmpty(cellData, "cellData is empty.");
        if (cellData.endsWith("\n")) {
            cellData = cellData.trim().replaceAll("\n", "");
        }
        if (cellData.indexOf("E+") > 0 || cellData.indexOf("e+") > 0) {
            double value = Double.parseDouble(cellData);
            cellData = "" + Double.valueOf(value).longValue();
        }
        return cellData;
    }

    public static enum ItemCfgTypeEnum {
        INTEGER16,

        UNSIGNED16,

        BOOLEAN1,

        UNSIGNED8,

        UNSIGNED32;
    }

    public static enum ItemDirectionTypeEnum {
        rx,

        tx;
    }

    @Getter
    @Setter
    public static class ItemCfg {

        /** 索引号 */
        private int id;

        /** HMI */
        private String system;

        /** 变量名 */
        private String name;

        /** 记录变量名 */
        private String varName;

        /** 必须记录 */
        private boolean required;

        /** 变量类型 */
        private ItemCfgTypeEnum type;

        /** 字偏移 */
        private int byteOffset;

        /** 字节位偏移 */
        private int bytePosOffset;

        /** 记录周期 */
        private String period;

        /** 方向 */
        private ItemDirectionTypeEnum direction;

        /** 常用变量顺序 */
        private int ordinal;
    }
}